package com.breeze.components.core.config;

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import com.breeze.components.core.config.peroperties.ThreadPoolProperties;
import com.breeze.components.core.util.Threads;
import jakarta.annotation.PreDestroy;
import java.util.concurrent.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

/**
 * 线程池配置
 *
 * @author breeze
 */
@AutoConfiguration
@EnableConfigurationProperties(ThreadPoolProperties.class)
@Slf4j
public class ThreadPoolConfig {

  public static final String MANY_POOL = "slowAndManyThreadPoolExecutor";

  /** 核心线程数 = cpu 核心数 + 1 */
  private final int core = Runtime.getRuntime().availableProcessors() + 1;

  private ScheduledExecutorService scheduledExecutorService;

  @Bean("defaultThreadPoolExecutor")
  public Executor defaultThreadPoolExecutor(ThreadPoolProperties threadPoolProperties) {
    return new ThreadPoolExecutor(
        threadPoolProperties.getCorePoolSize(),
        threadPoolProperties.getMaximumPoolSize(),
        threadPoolProperties.getKeepAliveTime(),
        TimeUnit.SECONDS,
        new LinkedBlockingDeque<>(threadPoolProperties.getQueueCapacity()),
        Executors.defaultThreadFactory(),
        new ThreadPoolExecutor.CallerRunsPolicy());
  }

  @Primary
  @Bean(MANY_POOL)
  public Executor slowAndManyThreadPoolExecutor(ThreadPoolProperties threadPoolProperties) {
    int processors = Runtime.getRuntime().availableProcessors();
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(processors);
    executor.setMaxPoolSize(processors * 3);
    executor.setKeepAliveSeconds(Math.toIntExact(threadPoolProperties.getKeepAliveTime()));
    executor.setQueueCapacity(processors * 30);
    executor.setThreadFactory(ThreadUtil.createThreadFactory(MANY_POOL + StrUtil.DASHED));
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    return executor;
  }

  /** 设置执行定时任务的线程池 */
  @Bean(name = "springScheduledExecutorService")
  public ThreadPoolTaskScheduler springScheduledExecutorService(
      ThreadPoolProperties threadPoolProperties) {
    ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
    scheduler.setPoolSize(threadPoolProperties.getCorePoolSize());
    scheduler.setWaitForTasksToCompleteOnShutdown(true);
    scheduler.setAwaitTerminationSeconds(30);
    scheduler.setThreadFactory(Executors.defaultThreadFactory());
    scheduler.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    return scheduler;
  }

  /** 执行周期性或定时任务 */
  @Bean(name = "scheduledExecutorService")
  protected ScheduledExecutorService scheduledExecutorService() {
    ScheduledThreadPoolExecutor scheduledThreadPoolExecutor =
        new ScheduledThreadPoolExecutor(
            core,
            new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(),
            new ThreadPoolExecutor.CallerRunsPolicy()) {
          @Override
          protected void afterExecute(Runnable r, Throwable t) {
            super.afterExecute(r, t);
            Threads.printException(r, t);
          }
        };
    this.scheduledExecutorService = scheduledThreadPoolExecutor;
    return scheduledThreadPoolExecutor;
  }

  /** 销毁事件 */
  @PreDestroy
  public void destroy() {
    try {
      log.info("====关闭后台任务任务线程池====");
      Threads.shutdownAndAwaitTermination(scheduledExecutorService);
    } catch (Exception e) {
      log.error(e.getMessage(), e);
    }
  }
}
